﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Interpreter.cs" company="dimamartovoi">
//   Apache License
//   Version 2.0, January 2004
//   http://www.apache.org/licenses
// </copyright>
// <summary>
//   Defines the Interpreter type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;

#if !DEBUG
using System.IO;
#endif

using System.Reflection;
using System.Reflection.Emit;

#if DEBUG
using System.Threading;
#endif

using ActorModel.Evolution.Grammar;
using ActorModel.Evolution.Operations;

using Antlr.Runtime;

using Common.Logging;

namespace ActorModel.Evolution
{
    /// <summary>
    ///     The interpreter.
    /// </summary>
    public class Interpreter
    {
        #region Public Methods and Operators

        /// <summary>
        /// The interprete.
        /// </summary>
        /// <param name="code">
        /// The code.
        /// </param>
        /// <returns>
        /// The <see cref="Func{T1,T2,TResult}"/>.
        /// </returns>
        public Func<string, IEnumerable<string>, IEnumerable<string>> Interprete(string code)
        {
            Type returnType = typeof(IEnumerable<string>);
            Type[] argTypes = { typeof(string), typeof(IEnumerable<string>) };

#if DEBUG

            var domain = Thread.GetDomain();
            var name = new AssemblyName("Debug");
            var assmBuilder = domain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
            var moduleBuilder = assmBuilder.DefineDynamicModule("DebugModule", name.Name + ".dll");
            var typeBuilder = moduleBuilder.DefineType("DebugEvolution", TypeAttributes.Public);
            var methodBuilder = typeBuilder.DefineMethod(
                "Debug", MethodAttributes.Public | MethodAttributes.Static, returnType, argTypes);
            var ilGen = methodBuilder.GetILGenerator();
            var statesBuilder = ilGen.DeclareLocal(typeof(string[]));

            var countMethod = typeof(SystemOperations).GetMethod("ResultStatesCount");

            ilGen.Emit(OpCodes.Nop);
            ilGen.Emit(OpCodes.Ldarg_1);
            ilGen.Emit(OpCodes.Call, countMethod);
            ilGen.Emit(OpCodes.Newarr, typeof(string));
            ilGen.Emit(OpCodes.Stloc, statesBuilder);

            var codeStream = new ANTLRStringStream(code);
            var lexer = new EvolutionLexer(codeStream);
            var tokenStream = new CommonTokenStream(lexer);
            var parser = new EvolutionParser(tokenStream);
            parser.evolutionFunction(ilGen, LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType), statesBuilder);

            typeBuilder.CreateType();
            assmBuilder.Save(name.Name + ".dll");

            return null;
#else

            var dynamicMethod = new DynamicMethod(Guid.NewGuid().ToString(), returnType, argTypes);
            ILGenerator ilGen = dynamicMethod.GetILGenerator();
            LocalBuilder statesBuilder = ilGen.DeclareLocal(typeof(string[]));

            MethodInfo countMethod = typeof(SystemOperations).GetMethod("ResultStatesCount");

            ilGen.Emit(OpCodes.Nop);
            ilGen.Emit(OpCodes.Ldarg_1);
            ilGen.Emit(OpCodes.Call, countMethod);
            ilGen.Emit(OpCodes.Newarr, typeof(string));
            ilGen.Emit(OpCodes.Stloc, statesBuilder);

            var codeStream = new ANTLRStringStream(code);
            var lexer = new EvolutionLexer(codeStream);
            var tokenStream = new CommonTokenStream(lexer);
            var parser = new EvolutionParser(tokenStream);

            TextWriter standartErrorOutput = Console.Error;
            if (!Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), "logs")))
            {
                Directory.CreateDirectory(Path.Combine(Directory.GetCurrentDirectory(), "logs"));
            }

            string parserErrorsLogPath = Path.Combine(
                Directory.GetCurrentDirectory(), "logs/_debug.parsing.syntax.evollog");
            using (var fileStream = new FileStream(parserErrorsLogPath, FileMode.Create))
            {
                using (var streamWriter = new StreamWriter(fileStream))
                {
                    ILog log = LogManager.GetLogger("debug.parsing");
                    Console.SetError(streamWriter);
                    parser.evolutionFunction(ilGen, log, statesBuilder);
                }
            }

            Console.SetError(standartErrorOutput);

            var handler =
                (Func<string, IEnumerable<string>, IEnumerable<string>>)
                dynamicMethod.CreateDelegate(typeof(Func<string, IEnumerable<string>, IEnumerable<string>>));
            return handler;

#endif
        }

        #endregion
    }
}